#include "common_config_manager.h"
#include "base_log.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <libgen.h>
#include <string.h>
#include <errno.h>

namespace El {
namespace Common {

ConfigManager &ConfigManager::GetInstance()
{
    static ConfigManager instance;
    return instance;
}

ConfigManager::ConfigManager() {}

ConfigManager::~ConfigManager() {}

int32_t ConfigManager::Register(const std::string &key, std::function<bool(const nlohmann::json &)> listener)
{
    return listeners_[key].Register(listener);
}

void ConfigManager::Unregister(const std::string &key, int32_t listenerId)
{
    auto it = listeners_.find(key);
    if (it != listeners_.end()) {
        it->second.Unregister(listenerId);
        if (it->second.IsEmpty()) {
            listeners_.erase(it);
        }
    }
}

int32_t ConfigManager::RegisterVerify(const std::string &key, std::function<bool(const nlohmann::json &)> listener)
{
    return listenersVerify_[key].Register(listener);
}

void ConfigManager::UnregisterVerify(const std::string &key, int32_t listenerId)
{
    auto it = listenersVerify_.find(key);
    if (it != listenersVerify_.end()) {
        it->second.Unregister(listenerId);
    }
}

bool ConfigManager::NotifyListeners(const std::string &key, const nlohmann::json &value)
{
    auto itVerify = listenersVerify_.find(key);
    if (itVerify != listenersVerify_.end()) {
        if (!itVerify->second.InvokeAll(value)) {
            LOG_ERROR("Config validation failed for key: {}", key.c_str());
            return false;
        }
    }

    auto it = listeners_.find(key);
    if (it != listeners_.end()) {
        if (!it->second.InvokeAll(value)) {
            LOG_ERROR("Config listener failed for key: {}", key.c_str());
            return false;
        }
    }
    return true;
}

bool ConfigManager::LoadConfig(const std::string &filename, const std::string &defaultConfigFilename)
{
    filename_ = filename;
    defaultFilename_ = defaultConfigFilename;
    if (!LoadDefaultConfig(defaultConfigFilename)) {
        LOG_ERROR("Failed to load default config file: {}", defaultConfigFilename.c_str());
    }

    if (!LoadConfigFromFile(filename)) {
        LOG_ERROR("Failed to load config file: {}", filename.c_str());
        if (!LoadConfigFromFile(filename + ".bak")) {
            LOG_ERROR("Failed to load backup config file: {}", (filename + ".bak").c_str());
            return false;
        }
    }

    MergeConfigs(defaultConfig_);
    return true;
}

bool ConfigManager::LoadDefaultConfig(const std::string &defaultConfigFilename)
{
    std::ifstream file(defaultConfigFilename);
    if (file.is_open()) {
        try {
            std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
            file.close();
            defaultConfig_ = nlohmann::json::parse(fileContent, nullptr, false);
            if (defaultConfig_.is_discarded()) {
                LOG_ERROR("Failed to parse default config file: {}", defaultConfigFilename.c_str());
                defaultConfig_ = nlohmann::json::object();
                return false;
            }
            if (!defaultConfig_.is_object()) {
                LOG_ERROR("Default config must be a JSON object: {}", defaultConfigFilename.c_str());
                defaultConfig_ = nlohmann::json::object();
                return false;
            }
        } catch (const std::exception &e) {
            LOG_ERROR("Exception while parsing default config: {} - {}", defaultConfigFilename.c_str(), e.what());
            defaultConfig_ = nlohmann::json::object();
            return false;
        }
    } else {
        LOG_ERROR("Failed to open default config file: {}", defaultConfigFilename.c_str());
        defaultConfig_ = nlohmann::json::object();
        return false;
    }
    return true;
}

void ConfigManager::MergeConfigs(const nlohmann::json &defaultConfig)
{
    if (!defaultConfig.is_object()) {
        LOG_ERROR("Cannot merge configs: default config is not an object");
        return;
    }

    std::lock_guard<std::mutex> lock(mutex_);
    try {
        for (const auto &[key, value] : defaultConfig.items()) {
            if (config_.find(key) == config_.end()) {
                config_[key] = value;
            }
        }
    } catch (const std::exception &e) {
        LOG_ERROR("Exception while merging configs: {}", e.what());
    }
}

bool ConfigManager::LoadConfigFromFile(const std::string &filename)
{
    std::ifstream file(filename);
    if (file.is_open()) {
        std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        file.close();
        std::lock_guard<std::mutex> lock(mutex_);
        config_ = nlohmann::json::parse(fileContent, nullptr, false);
        if (config_.is_discarded()) {
            return false;
        }
    } else {
        return false;
    }
    return true;
}

bool ConfigManager::SaveConfig(const std::string &filename)
{
    if (!SaveConfigToFile(filename)) {
        return false;
    }
    BackupConfig(filename);
    return true;
}

bool ConfigManager::SaveConfigToFile(const std::string &filename)
{
    // 创建目录
    char *pathCopy = strdup(filename.c_str());
    char *dir = dirname(pathCopy);
    std::string currentPath = "";
    char *savePtr = nullptr;
    char *token = strtok_r(dir, "/", &savePtr);

    while (token != nullptr) {
        currentPath += "/";
        currentPath += token;
        if (mkdir(currentPath.c_str(), 0755) != 0 && errno != EEXIST) {
            LOG_ERROR("Failed to create directory: {}, error: {}", currentPath, strerror(errno));
            free(pathCopy);
            return false;
        }
        token = strtok_r(nullptr, "/", &savePtr);
    }
    free(pathCopy);

    std::ofstream file(filename, std::ios::out | std::ios::binary | std::ios::trunc);
    if (!file.is_open()) {
        LOG_ERROR("Failed to open config file: {}", filename.c_str());
        return false;
    }

    {
        std::lock_guard<std::mutex> lock(mutex_);
        file << config_.dump(4);
    }

    file.flush();
    if (file.fail()) {
        LOG_ERROR("Failed to write config file: {}", filename.c_str());
        file.close();
        return false;
    }
    file.close();
    return true;
}

void ConfigManager::BackupConfig(const std::string &filename)
{
    std::string backupFilename = filename + ".bak";
    std::ifstream src(filename, std::ios::binary);
    std::ofstream dst(backupFilename, std::ios::binary);
    dst << src.rdbuf();
    src.close();
    dst.close();
}

nlohmann::json ConfigManager::GetConfig(const std::string &key)
{
    std::lock_guard<std::mutex> lock(mutex_);
    auto it = config_.find(key);
    if (it != config_.end()) {
        return it.value();
    } else {
        LOG_WARN("Key not found in main config, checking default config: {}", key.c_str());
        auto defaultIt = defaultConfig_.find(key);
        if (defaultIt != defaultConfig_.end()) {
            return defaultIt.value();
        } else {
            LOG_ERROR("Key not found in both main and default configs: {}", key.c_str());
            return {};
        }
    }
}

bool ConfigManager::SetConfig(const std::string &key, const nlohmann::json &value)
{
    if (!NotifyListeners(key, value)) {
        return false;
    }

    {
        std::lock_guard<std::mutex> lock(mutex_);
        config_[key] = value;
    }
    SaveConfig(filename_);
    return true;
}

nlohmann::json ConfigManager::GetDefaultConfig(const std::string &key)
{
    std::lock_guard<std::mutex> lock(mutex_);
    auto it = defaultConfig_.find(key);
    if (it != defaultConfig_.end()) {
        return it.value();
    } else {
        LOG_DEBUG("Key not found in default config: {}", key.c_str());
        return {};
    }
}

bool ConfigManager::SetDefaultConfig(const std::string &key, const nlohmann::json &value)
{
    {
        std::lock_guard<std::mutex> lock(mutex_);
        defaultConfig_[key] = value;
    }

    // 保存默认配置到文件
    SaveDefaultConfig(defaultFilename_);

    // 如果config_中没有这一项，则添加到config_中
    if (config_.find(key) == config_.end()) {
        config_[key] = value;
        SaveConfig(filename_);
    }

    return true;
}

bool ConfigManager::SaveDefaultConfig(const std::string &filename)
{
    std::ofstream file(filename);
    if (file.is_open()) {
        std::lock_guard<std::mutex> lock(mutex_);
        file << defaultConfig_.dump(4);
        file.close();
        return true;
    } else {
        LOG_ERROR("Failed to open default config file for writing: {}", filename.c_str());
        return false;
    }
}

} // namespace Common
} // namespace El